﻿/* =====================================================================================================
* FileName: AsyncSocketConnect            
* Created By: 马志
* Created Date: 2015年12月16日
* Email：hautmz@163.com
* Modified By:
* Modified Date:
* Copyright (c) 2010 - 2015 北京其乐融融科技有限公司
* ===================================================================================================== */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;


namespace PluginService.Common.Sockets
{


    public delegate void HandleMessageHandler<T>(AsyncMessageHandleArgs<T> args) where T:class ;

    public delegate void ExceptionHandler(object sender, Exception e);

    public delegate void SocketInvalidedHandler<T>(AsyncSocketConnect<T> connect, string errMsg) where T:class ;

    public delegate object HandleRequestHandler<T>(T args);

    public class AsyncSocketConnect<T> where T :class 
    {
        #region 变量

        protected AsnySocketType _type = AsnySocketType.Client;
        protected string _id = null;
        protected Socket _socket = null;
        protected Encoding _encoding = UTF8Encoding.UTF8;
        //protected TransferMode _TransferMode = TransferMode.Stream;
        protected bool _actived = false;

        protected IRecieveArgs<T> _recieveArgs;

        #endregion

        #region  事件

        public event HandleMessageHandler<T> OnReceivedMessage;
        public event ExceptionHandler OnError;
        public event SocketInvalidedHandler<T> OnSocketInvalided;
        //public event HandleRequestHandler<T> OnRequestHandle;

        #endregion

        #region 属性

        public IRecieveArgs<T> IRecieveArgs {
            set { _recieveArgs = value; }
            get { return _recieveArgs; }
        }

        public string ID {
            get { return _id; }
        }

        public Socket Socket
        {
            get
            {
                return _socket;
            }
        }

        public Encoding Encoding
        {
            get
            {
                return _encoding;
            }
            set
            {
                _encoding = value;
            }
        }

        public bool Actived
        {
            get
            {
                return _actived;
            }
            set { _actived = value; }
        }

        #endregion

        #region 静态公共函数

        /// <summary>
        /// 建立一个客户端的异步Socket通信对象
        /// </summary>
        /// <returns></returns>

        public static AsyncSocketConnect<T> CreateClientSocket()
        {
            AsyncSocketConnect<T> asynSocket = new AsyncSocketConnect<T>();
            asynSocket._type = AsnySocketType.Client;
            asynSocket._id = Guid.NewGuid().ToString();
            //asynSocket._TransferMode = transferMode;
            asynSocket._socket = new Socket(
                AddressFamily.InterNetwork,
                SocketType.Stream,
                ProtocolType.Tcp);

            IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);
            asynSocket._socket.Bind(endPoint);

            return asynSocket;
        }

        /// <summary>
        /// 为客户端发起到服务器端连接建立一个异步Socket通信对象
        /// </summary>
        /// <param name="acceptSocket"></param>
        /// <returns></returns>
        public static AsyncSocketConnect<T> CreateAcceptSocket(Socket acceptSocket)
        {
            AsyncSocketConnect<T> asynSocket = new AsyncSocketConnect<T>();
            asynSocket._type = AsnySocketType.Server;
            asynSocket._id = Guid.NewGuid().ToString();
            //asynSocket._TransferMode = transferMode;
            asynSocket._socket = acceptSocket;
            asynSocket._actived = true;
            //asynSocket.BeginReciveData();
            return asynSocket;
        }

        #endregion

        #region 公共函数

        public void ConnectToServer(string hostName, int port)
        {
            CheckClient("ConnectTo方法");
            _socket.Connect(hostName, port);
            _actived = true;
            BeginReciveData();
        }


        public void AsyncSend(byte[] buffer)
        {
            try
            {
                _socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, SendCallBack, null);
            }
            catch { };
        }

        public void Close()
        {
            try
            {
                _socket.Shutdown(SocketShutdown.Both);
                _socket.Close();
            }
            catch(Exception e)
            {
                HandlerError(e);
            }
        }

        #endregion

        #region 受保护方法

        public void BeginReciveData()
        {
            try
            {
                //var args = CreateReciveArgsInstance();
                if (_recieveArgs==null)
                {
                    throw new NullReferenceException("未设置IRecieveArgs");
                }
                _socket.BeginReceive(_recieveArgs.Buffer, 0, _recieveArgs.BufferSize, SocketFlags.None, ReciveCallBack, _recieveArgs);

            }
            catch (Exception e)
            {
                
                HandlerError(e);
            }
        }

        protected void ReciveCallBack(IAsyncResult iar)
        {
            var args = iar.AsyncState as IRecieveArgs<T>;
            try
            {
                /*
                 * System.ObjectDisposedException:System.Net.Sockets.Socket
                 */
                var len = _socket.EndReceive(iar);
                if (len > 0)
                {
                    if (args != null)
                    {
                        args.RefreshData(len);
                        HandlerRecieveCallBack(args);
                    }
                }
            }
            catch (SocketException se)
            {
                _actived = false;
                Reset();
                if (OnSocketInvalided != null)
                    OnSocketInvalided(this, se.Message);
            }
            catch (Exception e)
            {
                try
                {
                    _socket.BeginReceive(args.Buffer, 0, args.BufferSize, SocketFlags.None, ReciveCallBack, args);
                }
                catch { }
                HandlerError(e);
                //throw;
            }
        }

        protected void HandlerError(Exception e)
        {
            if (OnError!=null)
            {
                OnError(this, e);
            }
        }

        #endregion

        #region 保护的虚函数

        protected virtual IRecieveArgs<T> CreateReciveArgsInstance()
        {
            throw new NotImplementedException();
        }

        protected virtual void HandlerRecieveCallBack(IRecieveArgs<T> args)
        {
            if (args.RecieveEnd())
            {
                var msg = args.GetRecieveMessage();
                var msgArgs = new AsyncMessageHandleArgs<T>(this, msg);

                Action<AsyncMessageHandleArgs<T>> del = (a) => { };
                del.BeginInvoke(msgArgs, (ar) => {
                    if (OnReceivedMessage!=null)
                    {
                        OnReceivedMessage(msgArgs);
                    }

                    del.EndInvoke(ar);
                }, msgArgs);
                
            }
            _socket.BeginReceive(args.Buffer, 0, args.BufferSize, SocketFlags.None, ReciveCallBack, args);
        }

        #endregion

        #region 私有方法

        private void CheckServer(string actionName)
        {
            if (_type == AsnySocketType.Client)
                throw new Exception(string.Format("客户端连接不支持{0}", actionName));
        }

        private void CheckClient(string actionName)
        {
            if (_type == AsnySocketType.Server)
                throw new Exception(string.Format("服务器端连接不支持{0}", actionName));
        }

        private void Reset()
        {
            if (_type == AsnySocketType.Client)
            {
                _socket = new Socket(
                    AddressFamily.InterNetwork,
                    SocketType.Stream,
                    ProtocolType.Tcp);
                IPAddress address = Dns.GetHostAddresses("127.0.0.1")[0];
                IPEndPoint endPoint = new IPEndPoint(address, 0);
                _socket.Bind(endPoint);
            }
        }

        private void SendCallBack(IAsyncResult iar)
        {
            try
            {
                _socket.EndSend(iar);
            }
            catch (SocketException se)
            {
                if (OnSocketInvalided != null)
                    OnSocketInvalided(this, se.Message);
                _actived = false;
            }
            catch (Exception e)
            {
                HandlerError(e);
            }
        }

        #endregion
    }
}